a tool for shared writing and social publishing
1"use server";
2
3import {
4 AppBskyRichtextFacet,
5 Agent as BskyAgent,
6 UnicodeString,
7} from "@atproto/api";
8import sharp from "sharp";
9import { TID } from "@atproto/common";
10import { getIdentityData } from "actions/getIdentityData";
11import { AtpBaseClient, PubLeafletDocument } from "lexicons/api";
12import { createOauthClient } from "src/atproto-oauth";
13import { supabaseServerClient } from "supabase/serverClient";
14import { Json } from "supabase/database.types";
15import {
16 getMicroLinkOgImage,
17 getWebpageImage,
18} from "src/utils/getMicroLinkOgImage";
19
20export async function publishPostToBsky(args: {
21 text: string;
22 url: string;
23 title: string;
24 description: string;
25 document_record: PubLeafletDocument.Record;
26 rkey: string;
27 facets: AppBskyRichtextFacet.Main[];
28}) {
29 const oauthClient = await createOauthClient();
30 let identity = await getIdentityData();
31 if (!identity || !identity.atp_did) return null;
32
33 let credentialSession = await oauthClient.restore(identity.atp_did);
34 let agent = new AtpBaseClient(
35 credentialSession.fetchHandler.bind(credentialSession),
36 );
37 let newPostUrl = args.url;
38 let preview_image = await getWebpageImage(newPostUrl, {
39 width: 1400,
40 height: 733,
41 noCache: true,
42 });
43
44 let binary = await preview_image.blob();
45 let resized_preview_image = await sharp(await binary.arrayBuffer())
46 .resize({
47 width: 1200,
48 fit: "cover",
49 })
50 .webp({ quality: 85 })
51 .toBuffer();
52
53 let blob = await agent.com.atproto.repo.uploadBlob(resized_preview_image, {
54 headers: { "Content-Type": binary.type },
55 });
56 let bsky = new BskyAgent(credentialSession);
57 let post = await bsky.app.bsky.feed.post.create(
58 {
59 repo: credentialSession.did!,
60 rkey: TID.nextStr(),
61 },
62 {
63 text: args.text,
64 createdAt: new Date().toISOString(),
65 facets: args.facets,
66 embed: {
67 $type: "app.bsky.embed.external",
68 external: {
69 uri: args.url,
70 title: args.title,
71 description: args.description,
72 thumb: blob.data.blob,
73 },
74 },
75 },
76 );
77 let record = args.document_record;
78 record.postRef = post;
79
80 let { data: result } = await agent.com.atproto.repo.putRecord({
81 rkey: args.rkey,
82 repo: credentialSession.did!,
83 collection: args.document_record.$type,
84 record,
85 validate: false, //TODO publish the lexicon so we can validate!
86 });
87 await supabaseServerClient
88 .from("documents")
89 .update({
90 data: record as Json,
91 })
92 .eq("uri", result.uri);
93 return true;
94}